﻿using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Mbs.ComponentModel.Container;
using Mbs.ComponentModel.Container.Injection;
using Mbs.ComponentModel.Container.Lifestyle;
using Mbs.ComponentModel.Container.Listner;
using Mbs.ComponentModel.Container.Loader;
using NUnit.Framework;

namespace Mbs.Test.IoC
{
    [TestFixture]
    public class KernelTest:TestBase
    {
        protected override void Init()
        {
            Person.HasVisited = LazyPerson.HasVisited = false;
        }

        [Test]
        public void RegisterTest()
        {
            kernel.Register<IPerson,Person>("person", LifestyleType.Singleton, true);

            Assert.IsTrue(kernel.HasRegister("person"));
            Assert.IsTrue(kernel.HasRegister<IPerson>());
        }

        [Test]
        public void RegisterByFactoryTest()
        {
            kernel.Register<IPerson>("person", ctx => new Person());

            Assert.IsTrue(kernel.HasRegister("person"));
            Assert.IsTrue(kernel.HasRegister<IPerson>());

            var person = kernel.Get<IPerson>("person");
            Assert.IsTrue(person != null);

            var person2 = kernel.Get<Person>("person");
            Assert.IsTrue(person2 != null);

            Assert.AreSame(person, person2);
            Assert.IsTrue(Person.HasVisited);
        }



        [Test]
        public void GetByIdTest()
        {
            kernel.Register<IPerson,Person>("person", LifestyleType.Singleton, true);

            var person = kernel.Get<IPerson>("person");
            Assert.IsTrue(person != null);

            var person2 = kernel.Get<Person>("person");
            Assert.IsTrue(person2 != null);

            Assert.AreSame(person, person2);
            Assert.IsTrue(Person.HasVisited);
        }

        [Test]
        public void GetByTypeTest()
        {
            kernel.Register<IPerson,Person>("person", LifestyleType.Singleton, true);

            var person = kernel.Get<IPerson>();
            Assert.IsTrue(person != null);

            var person2 = kernel.Get<IPerson,Person>();
            Assert.IsTrue(person2 != null);

            Assert.AreSame(person, person2);
            Assert.IsTrue(Person.HasVisited);
        }

        [Test]
        public void GetAllTest()
        {
            kernel.Register<IPerson,Person>("person", LifestyleType.Singleton, true);
            kernel.Register(typeof(IPerson), typeof(Person2));

            var person = kernel.Get<IPerson>();
            Assert.IsTrue(person != null);
            Assert.IsTrue(typeof(IPerson).IsAssignableFrom(person.GetType()));

            var items = kernel.GetAll<IPerson>().ToArray();
            Assert.IsTrue(items.Length == 2);
            Assert.IsTrue(items[0] is Person);
            Assert.IsTrue(items[1] is Person2);

        }

        [Test]
        public void InspectCacheProxyTest()
        {
            injectMgr.Register(new PropertyInjectStragegy());

            TransientLifestyleTest();
        }

        [Test]
        public void TransientLifestyleTest()
        {
            kernel.Register<IPerson,Person>("person",LifestyleType.Transient, true);

            var person = kernel.Get(typeof(IPerson)) as IPerson;
            Assert.IsTrue(person != null);

            var person2 = kernel.Get(typeof(IPerson)) as Person;
            Assert.IsTrue(person2 != null);

            Assert.AreNotSame(person, person2);
            Assert.IsTrue(Person.HasVisited);
        }

        [Test]
        public void PerThreadLifestyleTest()
        {
            kernel.Register<IPerson, Person>("person", LifestyleType.Thread, true);

            IPerson person = null,
                person2 = null, person3 = null, person4 = null;

            PopulatePerThreadLifestyle(ref person,ref person2);

            ManualResetEvent mre = new ManualResetEvent(false);
            ThreadPool.QueueUserWorkItem((s) =>
                {

                    PopulatePerThreadLifestyle(ref person3, ref person4);

                    mre.Set();
                });
            mre.WaitOne();

            Assert.AreSame(person, person2);
            Assert.AreSame(person3, person4);
            Assert.AreNotSame(person, person3);
        }

        private void PopulatePerThreadLifestyle(ref IPerson person, ref IPerson person2)
        {
            person = kernel.Get(typeof(IPerson)) as IPerson;
            Assert.IsTrue(person != null);

            person2 = kernel.Get(typeof(IPerson)) as Person;
            Assert.IsTrue(person2 != null);

            
        }

        [Test]
        public void LazyInitTest()
        {
            bool hasLazyInit = true;
            kernel.Register<IPerson,Person>("person", LifestyleType.Singleton, hasLazyInit);

            Assert.IsFalse(Person.HasVisited);

            hasLazyInit = false;
            kernel.Register<ILazyPerson, LazyPerson>("lazyPerson",LifestyleType.Singleton, hasLazyInit);

            Assert.IsTrue(LazyPerson.HasVisited);
        }

        [Test]
        public void RegisterInstanceTest()
        {
            var p = new Person();
            p.Name = "Test !";
            kernel.RegisterInstance<IPerson,Person>("person",p);

            Assert.IsTrue(kernel.HasRegister("person"));
            Assert.IsTrue(kernel.HasRegister<IPerson>());

            var person = kernel.Get<IPerson>("person");
            Assert.IsNotNull(person);
            Assert.AreSame(p, person);
        }

        [Test]
        public void StartableListnerTest()
        {
            kernel.ListnerManager.Register(new StartableListner());

            kernel.Register("person", typeof(IPerson), typeof(StartablePerson));

            var person = kernel.Get(typeof(IPerson)) as StartablePerson;
            Assert.IsTrue(person != null);
            Assert.IsTrue(person.HasStarted);
            Assert.IsFalse(person.HasStopped);
        }

        [Test]
        public void ConstructorInjectTest()
        {
            kernel.Register("person", typeof(IPerson), typeof(Person2));
            kernel.Register("horse", typeof(IHorse), typeof(RedHorse));

            var person = kernel.Get("person") as Person2;
            Assert.IsTrue(person != null);
            Assert.IsNotNull(person.Horse);
        }

        [Test]
        public void ConstructorInject2Test()
        {
            kernel.Register("person", typeof(IPerson), typeof(Person5));
            kernel.Register("horse", typeof(IHorse), typeof(RedHorse));
            kernel.Register("A", typeof(A), typeof(A));
            kernel.RegisterInstance("str", typeof(string), "ZhangSan");


            var person = kernel.Get("person") as Person5;
            Assert.IsTrue(person != null);
            Assert.IsNotNull(person.Horse);
            Assert.IsNotNull(person.A);
            Assert.IsTrue(person.HasVisited);
            Assert.IsTrue(string.IsNullOrEmpty(person.Name));
        }

        [Test]
        public void ConstructorInject3Test()
        {
            kernel.Register("person", typeof(IPerson), typeof(Person6));
            kernel.Register("horse", typeof(IHorse), typeof(RedHorse));
            kernel.Register("A", typeof(A), typeof(A));
            kernel.RegisterInstance("str", typeof(string), "ZhangSan");


            var person = kernel.Get("person") as Person6;
            Assert.IsTrue(person != null);
            Assert.IsNotNull(person.Horse);
            Assert.IsNotNull(person.A);
            Assert.IsTrue(person.HasVisited);
            Assert.AreEqual("ZhangSan", person.Name);
        }

        [Test]
        public void AutoConstructorTest()
        {
            kernel.Register("person", typeof(IPerson), typeof(Person2));
            // kernel.Do("horse", typeof(IHorse), typeof(RedHorse), Lifestyle.Singleton, true);

            var person = kernel.Get("person") as Person2;
            Assert.IsTrue(person != null);
            Assert.IsNull(person.Horse);
        }

        [Test]
        public void NeedNamedParameterConstructorTest()
        {
            var ps = new Dictionary<string, object>();
            var person = new Person();

            ps["id"] = 10;
            ps["name"] = "ZhangSan";
            ps["person"] = person;

            kernel.Register(typeof(INeedNamedParameterConstructorInterface), typeof(NeedNamedParameterConstructorClass));

            var instance = kernel.Get(typeof(INeedNamedParameterConstructorInterface), ps) as INeedNamedParameterConstructorInterface;

            Assert.IsNotNull(instance);
            Assert.AreEqual(10, instance.Id);
            Assert.IsTrue("ZhangSan" == instance.Name);
            Assert.AreSame(person, instance.Person);

        }

        [Test]
        public void NeedParameterConstructorTest()
        {
            var person = new Person();

            kernel.Register(typeof(INeedNamedParameterConstructorInterface), typeof(NeedNamedParameterConstructorClass));

            var instance = kernel.Get(typeof(INeedNamedParameterConstructorInterface), 10, "ZhangSan", person) as INeedNamedParameterConstructorInterface;

            Assert.IsNotNull(instance);

            Assert.IsNotNull(instance);
            Assert.AreEqual(10, instance.Id);
            Assert.IsTrue("ZhangSan"== instance.Name);
            Assert.AreSame(person, instance.Person);
        }

        [Test]
        public void LoadingInSequence()
        {
            kernel.Register("C", typeof(C));
            kernel.Register("B", typeof(B));
            kernel.Register("A", typeof(A));

            Assert.IsNotNull(kernel.Get("A"));
            Assert.IsNotNull(kernel.Get("B"));
            Assert.IsNotNull(kernel.Get("C"));
        }



        [Test]
        public void LoadingPartiallyInSequence()
        {
            kernel.Register("A", typeof(A));
            kernel.Register("B", typeof(B));
            kernel.Register("C", typeof(C));

            Assert.IsNotNull(kernel.Get("A"));
            Assert.IsNotNull(kernel.Get("B"));
            Assert.IsNotNull(kernel.Get("C"));
        }

        [Test]
        public void LoadingOutOfSequenceWithExtraLoad()
        {
            kernel.Register("A", typeof(A));
            kernel.Register("B", typeof(B));
            kernel.Register("C", typeof(C));
            kernel.RegisterInstance("I", typeof(int), 5);

            Assert.IsNotNull(kernel.Get("A"));
            Assert.IsNotNull(kernel.Get("B"));
            Assert.IsNotNull(kernel.Get("C"));
            Assert.AreEqual(5, kernel.Get<C>().I);
        }

        [Test]
        public void AutoPropertyInjectTest()
        {
            injectMgr.Register(new PropertyInjectStragegy());

            kernel.Register(typeof(IPerson), typeof(Person2));
            kernel.Register(typeof(IHorse), typeof(RedHorse));

            var instance = kernel.Get<IPerson>() as Person2;

            Assert.IsNotNull(instance);
            Assert.IsNotNull(instance.Horse);
            Assert.IsTrue(instance.Horse is RedHorse);

        }

        [Test]
        public void RegisterByFactoryAndAutoPropertyInjectTest()
        {
            injectMgr.Register(new AutoPropertyInjectStragegy());

            kernel.Register<IPerson>(ctx => new Person2());
            kernel.Register<IHorse>(ctx => new RedHorse());

            var instance = kernel.Get<IPerson>() as Person2;

            Assert.IsNotNull(instance);
            Assert.IsNotNull(instance.Horse);
            Assert.IsTrue(instance.Horse is RedHorse);

        }

        [Test]
        public void FieldInjectTest()
        {
            injectMgr.Register(new FieldInjectStragegy());
            kernel.Register(typeof(IPerson), typeof(Person3));
            kernel.Register<FieldInjectionModel>((ctx) => new FieldInjectionModel());
            var instance = kernel.Get<FieldInjectionModel>();

            Assert.IsNotNull(instance);
            Assert.IsNotNull(instance.Person);
        }

        [Test]
        public void AutoFieldInjectTest()
        {
            injectMgr.Register(new AutoFieldInjectStragegy());
            kernel.Register(typeof(IPerson), typeof(Person3));
            kernel.Register<FieldInjectionModel>((ctx) => new FieldInjectionModel());
            var instance = kernel.Get<FieldInjectionModel>();

            Assert.IsNotNull(instance);
            Assert.IsNotNull(instance.Person);
        }


        [Test]
        public void PropertyInjectTest()
        {
            injectMgr.Register(new PropertyInjectStragegy());
            kernel.Register(typeof(IPerson), typeof(Person3));
            kernel.Register(typeof(IHorse), typeof(RedHorse));

            var instance = kernel.Get<IPerson>() as Person3;

            Assert.IsNotNull(instance);
            Assert.IsNotNull(instance.Horse);
            Assert.IsTrue(instance.HasVisited);
            Assert.IsTrue(instance.Horse is RedHorse);

        }

        [Test]
        public void MethodInjectTest()
        {
            injectMgr.Register(new MethodInjectStragegy());
            kernel.Register(typeof(IPerson), typeof(Person4));
            kernel.Register(typeof(IHorse), typeof(RedHorse));

            var instance = kernel.Get<IPerson,Person4>();

            Assert.IsNotNull(instance);
            Assert.IsNotNull(instance.Horse);
            Assert.IsTrue(instance.HasVisited);
            Assert.IsTrue(instance.Horse is RedHorse);

        }
        
       

        [Test]
        public void AssemblyLoaderTest()
        {
            kernel.LoaderManager.Register(new AssemblyLoader(typeof(IHorse).Assembly));
           injectMgr.Register(new MethodInjectStragegy());

            var instance = kernel.Get<IPerson,Person4>();

            Assert.IsNotNull(instance);
            Assert.IsNotNull(instance.Horse);
            Assert.IsTrue(instance.HasVisited);
            Assert.IsTrue(instance.Horse is RedHorse);
        }

        [Test]
        [ExpectedException(typeof(LoopDependencyException))]
        public void LoopDependencyTest()
        {
            injectMgr.Register(new AutoPropertyInjectStragegy());
            kernel.Register<_A, _A>()
                .Register<_B, _B>();
         
            kernel.Get<_A>();
        }

        [Test]
        public void GenericTest()
        {
            injectMgr.Register(new AutoPropertyInjectStragegy());

            kernel.Register(typeof(IList<>),typeof(List<>));
            kernel.Register(typeof(GenericCollection<>), typeof(GenericCollection<>));

            Assert.IsTrue(kernel.HasRegister(typeof(IList<>)));
            Assert.IsTrue(kernel.HasRegister(typeof(IList<int>)));
            Assert.IsTrue(kernel.HasRegister(typeof(IList<string>)));

            Assert.IsTrue(kernel.HasRegister(typeof(GenericCollection<>)));
            Assert.IsTrue(kernel.HasRegister(typeof(GenericCollection<int>)));
            Assert.IsTrue(kernel.HasRegister(typeof(GenericCollection<string>)));

            var coll = kernel.Get<GenericCollection<int>>();
            Assert.IsNotNull(coll);
            Assert.IsNotNull(coll.Items);

            var instance = kernel.Get<IList<int>>();
            Assert.IsNotNull(instance);
            Assert.AreSame(coll.Items, instance);

            var instance2 = kernel.Get<IList<int>>();
            Assert.IsNotNull(instance2);
            Assert.AreSame(coll.Items, instance2);


            var coll2 = kernel.Get<GenericCollection<string>>();
            var instance3 = kernel.Get<IList<string>>();
            Assert.IsNotNull(instance3);
            Assert.AreSame(coll2.Items, instance3);

            var instance4 = kernel.Get<IList<string>>();
            Assert.IsNotNull(instance4);
            Assert.AreSame(coll2.Items, instance4);


        }

        [Test]
        public void Generic2Test()
        {
            injectMgr.Register(new PropertyInjectStragegy());

            kernel.Register(typeof(IList<>), typeof(List<>));
            kernel.Register(typeof(GenericCollection2<>), typeof(GenericCollection2<>));

            Assert.IsTrue(kernel.HasRegister(typeof(IList<>)));
            Assert.IsTrue(kernel.HasRegister(typeof(IList<int>)));
            Assert.IsTrue(kernel.HasRegister(typeof(IList<string>)));

            Assert.IsTrue(kernel.HasRegister(typeof(GenericCollection2<>)));
            Assert.IsTrue(kernel.HasRegister(typeof(GenericCollection2<int>)));
            Assert.IsTrue(kernel.HasRegister(typeof(GenericCollection2<string>)));

            var coll = kernel.Get<GenericCollection2<int>>();
            Assert.IsNotNull(coll);
            Assert.IsTrue(coll.HasInjected);
            Assert.IsNotNull(coll.Items);

            var instance = kernel.Get<IList<int>>();
            Assert.IsNotNull(instance);
            Assert.AreSame(coll.Items, instance);

            var instance2 = kernel.Get<IList<int>>();
            Assert.IsNotNull(instance2);
            Assert.AreSame(coll.Items, instance2);


            var coll2 = kernel.Get<GenericCollection2<string>>();
            Assert.IsNotNull(coll2);
            Assert.IsTrue(coll2.HasInjected);
            Assert.IsNotNull(coll2.Items);

            var instance3 = kernel.Get<IList<string>>();
            Assert.IsNotNull(instance3);
            Assert.AreSame(coll2.Items, instance3);

            var instance4 = kernel.Get<IList<string>>();
            Assert.IsNotNull(instance4);
            Assert.AreSame(coll2.Items, instance4);


        }

        [Test]
        public void Generic3Test()
        {
           injectMgr.Register(new MethodInjectStragegy());

            kernel.Register(typeof(IList<>), typeof(List<>));
            kernel.Register(typeof(GenericCollection3<>), typeof(GenericCollection3<>));

            Assert.IsTrue(kernel.HasRegister(typeof(IList<>)));
            Assert.IsTrue(kernel.HasRegister(typeof(IList<int>)));
            Assert.IsTrue(kernel.HasRegister(typeof(IList<string>)));

            Assert.IsTrue(kernel.HasRegister(typeof(GenericCollection3<>)));
            Assert.IsTrue(kernel.HasRegister(typeof(GenericCollection3<int>)));
            Assert.IsTrue(kernel.HasRegister(typeof(GenericCollection3<string>)));

            var coll = kernel.Get<GenericCollection3<int>>();
            Assert.IsNotNull(coll);
            Assert.IsTrue(coll.HasInjected);
            Assert.IsNotNull(coll.Items);

            var instance = kernel.Get<IList<int>>();
            Assert.IsNotNull(instance);
            Assert.AreSame(coll.Items, instance);

            var instance2 = kernel.Get<IList<int>>();
            Assert.IsNotNull(instance2);
            Assert.AreSame(coll.Items, instance2);


            var coll2 = kernel.Get<GenericCollection3<string>>();
            Assert.IsNotNull(coll2);
            Assert.IsTrue(coll2.HasInjected);
            Assert.IsNotNull(coll2.Items);

            var instance3 = kernel.Get<IList<string>>();
            Assert.IsNotNull(instance3);
            Assert.AreSame(coll2.Items, instance3);

            var instance4 = kernel.Get<IList<string>>();
            Assert.IsNotNull(instance4);
            Assert.AreSame(coll2.Items, instance4);


        }

    


    }
}
